﻿namespace Sai.Core
{
	using System.Collections.Generic;
	using System.IO;
	using System.Linq;
	using System.Text;
	using System;

	/// <summary>
	/// Creates a product release from a folder specification.
	/// </summary>
	public class ProductReleaseBuilder
	{
		private string releaseName = string.Empty;
		private List<string> folders = new List<string>();
		private List<string> files = new List<string>();
		private string fileFilter = FileConstants.AssemblyFilter;
		private FileSystemValidationOption fileSystemValidation;

		public static ProductReleaseBuilder ProductRelease()
		{
			return new ProductReleaseBuilder();
		}

		public ProductReleaseBuilder Named(string name)
		{
			this.releaseName = name;

			return this;
		}

		public ProductReleaseBuilder ValidateFiles(FileSystemValidationOption validation)
		{
			this.fileSystemValidation = validation;

			return this;
		}
		
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "InFolder", Justification = "Cased as intended")]
		public ProductReleaseBuilder InFolder(string folder)
		{
			this.folders.Add(folder);

			return this;
		}

		public ProductReleaseBuilder WithFilesLike(string filter)
		{
			this.fileFilter = filter;

			return this;
		}

		public ProductReleaseBuilder WithFile(string file)
		{
			this.files.Add(file);

			return this;
		}

		public ProductReleaseBuilder WithFiles(IEnumerable<string> files)
		{
			this.files.AddRange(files);

			return this;
		}

		public ProductRelease Build()
		{
			var release = new ProductRelease { VersionName = this.releaseName };

			foreach (string folder in this.folders.Where(x => this.fileSystemValidation == FileSystemValidationOption.EnsureExists ? Directory.Exists(x) : true))
			{
				var files = new DirectoryInfo(folder).GetFiles(this.fileFilter);

				foreach (var file in files)
				{
					release.Add(CreateAssembly(file, this.fileSystemValidation));
				}
			}

			foreach (var file in this.files.Where(x => this.fileSystemValidation == FileSystemValidationOption.EnsureExists ? File.Exists(x) : true))
			{
				release.Add(CreateAssembly(new FileInfo(file), this.fileSystemValidation));
			}

			return release;
		}

		private static ProductAssembly CreateAssembly(FileInfo info, FileSystemValidationOption validation)
		{
			string fileName = info.FullName;
			DateTime fileDate = info.CreationTimeUtc;
			long fileLength = validation == FileSystemValidationOption.EnsureExists ? info.Length : 0L;
			string checksum = validation == FileSystemValidationOption.EnsureExists ? CreateChecksum(info.FullName) : string.Empty;

			return new ProductAssembly(
							fileName,
							fileDate,
							fileLength, 
							checksum);
		}

		private static string CreateChecksum(string path)
		{
			const string HexFormat = "X2";
			var builder = new StringBuilder();

			using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
			{
				using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
				{
					byte[] hash = md5.ComputeHash(fs);

					for (int i = 0; i < hash.Length; ++i)
					{
						builder.Append(hash[i].ToString(HexFormat));
					}
				}
			}

			return builder.ToString();
		}
	}
}
